package hujz.java.file_server._default;

import hujz.java.file_server.FileMeta;
import hujz.java.file_server.StorageInterface;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.io.*;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;


public class LocalDiskStorageImpl implements StorageInterface {

    public String uploadWithMessageDigest(FileMeta fileMeta, InputStream inputStream) {
        Object[] dstInfo = createFile();
        fileMeta.setStorageIdentify(identify());
        File file = (File) dstInfo[0];
        fileMeta.setUri((String) dstInfo[1]);
        FileOutputStream fileOutputStream = null;
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("md5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            // never
        }

        try {
            fileOutputStream = new FileOutputStream(file);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            byte[] buffer = new byte[1024 * 8];
            int n;
            long length = 0;
            while ((n = inputStream.read(buffer)) != -1) {
                length += n;
                bufferedOutputStream.write(buffer, 0, n);
                md.update(buffer, 0, n);
            }
            fileMeta.setSize(length);
            fileMeta.setSign(Base64.getEncoder().encodeToString(md.digest()));
            fileMeta.setUrl(file.toURI().toString());
            bufferedOutputStream.flush();
            bufferedOutputStream.close();
            inputStream.close();
            return (String) dstInfo[1];
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                // ignore
            }
        }
    }

    private Object[] createFile() {
        String saveFile = DateFormatUtils.format(new Date(), "yyyy/MM/dd") + "/" + UUID.randomUUID().toString();
        File file = new File(storageDirFile, saveFile);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return new Object[2];
        }
        return new Object[]{file, saveFile};
    }

    private String storageDir = "./";
    private File storageDirFile = new File(storageDir);

    public void setStorageDir(String storageDir) {
        this.storageDir = storageDir;
        this.storageDirFile = new File(storageDir);
    }

    @Override
    public String identify() {
        return "local";
    }

    @Override
    public String storage(FileMeta fileMeta, File file) {
        fileMeta.setStorageIdentify(identify());
        Object[] dstInfo = createFile();
        File dst = (File) dstInfo[0];
        fileMeta.setUrl(dst.toURI().getPath());
        fileMeta.setUri((String) dstInfo[1]);
        fileMeta.setSize(file.length());
        Path target = null;
        try {
            target = Files.move(file.toPath(), dst.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return (String) dstInfo[1];
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (target == null) {
            try {
                return storage(fileMeta, new FileInputStream(file));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public String storage(FileMeta fileMeta, InputStream inputStream) {
        return uploadWithMessageDigest(fileMeta, inputStream);
    }

    @Override
    public File file(String uri) {
        // FIXME 防止路径注入
        return new File(storageDirFile, uri);
    }

    @Override
    public OutputStream stream(String uri) {
        try {
            return new FileOutputStream(file(uri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Channel channel(String uri) {
        try {
            return FileChannel.open(file(uri).toPath(), StandardOpenOption.READ);
        } catch (IOException e) {
            return null;
        }
    }
}
